package com.gitee.wsl.jvm.okhttp.intercept.base

import com.gitee.wsl.jvm.ext.base.UTF8
import com.gitee.wsl.jvm.okhttp.OkHttpExt
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import java.io.IOException
import java.io.UnsupportedEncodingException
import java.net.URLDecoder
import java.net.URLEncoder
import java.util.*


/**
 *
 * 描述：动态拦截器
 * 主要功能是针对参数：<br></br>
 * 1.可以获取到全局公共参数和局部参数，统一进行签名sign<br></br>
 * 2.可以自定义动态添加参数，类似时间戳timestamp是动态变化的，token（登录了才有），参数签名等<br></br>
 * 3.参数值是经过UTF-8编码的<br></br>
 * 4.默认提供询问是否动态签名（签名需要自定义），动态添加时间戳等<br></br>
 *
 * @author xuexiang
 * @since 2018/6/20 上午1:35
 */

abstract class InjectParamsInterceptor : Interceptor {
    var httpUrl: HttpUrl? = null

    /**
     * 是否需要签名
     */
    var isSign = false

    /**
     * 是否需要追加时间戳
     */
    var isTimeStamp = false

    /**
     * 是否需要添加token
     */
    var isAccessToken = false

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        var request: Request = chain.request()
        if (ACTION_GET == request.method) {
            httpUrl = OkHttpExt.parseUrl(request.url.toString()).toHttpUrlOrNull()
            request = addGetParamsSign(request)
        } else if (ACTION_POST == request.method) {
            httpUrl = request.url
            request = addPostParamsSign(request)
        } else {
            request = createNewRequest(request.newBuilder())
        }
        return chain.proceed(request)
    }

    /**
     * 为get请求 添加签名和公共动态参数
     *
     * @param request 旧请求
     * @return 新请求
     */
    @Throws(UnsupportedEncodingException::class)
    private fun addGetParamsSign(request: Request): Request {
        var httpUrl = request.url
        val newBuilder = httpUrl.newBuilder()

        //获取原有的参数
        val nameSet = httpUrl.queryParameterNames
        val nameList: List<String> = ArrayList(nameSet)
        val oldParams = TreeMap<String, String>()
        for (i in nameList.indices) {
            val values = httpUrl.queryParameterValues(nameList[i])
            val value = if (values.isNotEmpty()) values[0] else ""
            oldParams[nameList[i]] = value!!
        }
        val nameKeys = listOf(nameList).toString()
        //拼装新的参数
        val newParams = updateDynamicParams(oldParams)
        //Utils.checkNotNull(newParams, "newParams==null")

        newParams.forEach { (key, value) ->
            val urlValue: String = URLEncoder.encode(value, UTF8.name())
            //原来的URl: https://xxx.xxx.xxx/app/chairdressing/skinAnalyzePower/skinTestResult?appId=10101
            //避免重复添加
            if (!nameKeys.contains(key)) {
                newBuilder.addQueryParameter(key, urlValue)
            }
        }

        httpUrl = newBuilder.build()
        return createNewRequest(request.newBuilder().url(httpUrl))
    }

    /**
     * 为post请求 添加签名和公共动态参数
     *
     * @param request 旧请求
     * @return 新请求
     */
    @Throws(UnsupportedEncodingException::class)
    private fun addPostParamsSign(request1: Request): Request {
        var request = request1
        when(request.body){
            is FormBody-> {
                val bodyBuilder = FormBody.Builder()
                var formBody = request.body as FormBody

                //原有的参数
                val oldParams = TreeMap<String, String>()
                for (i in 0 until formBody.size) {
                    oldParams[formBody.encodedName(i)] = formBody.encodedValue(i)
                }

                //拼装新的参数
                val newParams = updateDynamicParams(oldParams)
                //Utils.checkNotNull(newParams, "newParams == null")

                newParams.forEach { (key, value1) ->
                    val value: String = URLDecoder.decode(value1, UTF8.name())
                    bodyBuilder.addEncoded(key, value)
                }
                val url: String = OkHttpExt.createUrlFromParams(
                    OkHttpExt.parseUrl(httpUrl!!.toUrl().toString()),
                    newParams
                )
                formBody = bodyBuilder.build()
                request = createNewRequest(request.newBuilder().post(formBody))
            }
            is MultipartBody -> {
                var multipartBody = request.body as MultipartBody
                val bodyBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)
                val oldParts: List<MultipartBody.Part> = multipartBody.parts

                //拼装新的参数
                val newParts: MutableList<MultipartBody.Part> = ArrayList<MultipartBody.Part>(oldParts)
                val oldParams = TreeMap<String, String>()
                val newParams = updateDynamicParams(oldParams)

                newParams.forEach{ (key, value) ->
                    val part = MultipartBody.Part.createFormData(key, value)
                    newParts.add(part)
                }
                for (part in newParts) {
                    bodyBuilder.addPart(part)
                }
                multipartBody = bodyBuilder.build()
                request = createNewRequest(request.newBuilder().post(multipartBody))
            }
            is RequestBody->{
                val params = updateDynamicParams(TreeMap())
                val url: String = OkHttpExt.createUrlFromParams(
                    OkHttpExt.parseUrl(httpUrl!!.toUrl().toString()),
                    params
                )
                request = createNewRequest(request.newBuilder().url(url))
            }
        }
        return request
    }

    /**
     * 动态更新请求体的参数
     *
     * @param dynamicMap 动态参数
     * @return 返回新的参数集合
     */
    protected abstract fun updateDynamicParams(dynamicMap: TreeMap<String, String>): TreeMap<String, String>

    /**
     * 动态更新请求头
     *
     * @param builder 请求构建者
     * @return 返回新的请求构建者
     */
    protected fun updateHeaders(builder: Request.Builder): Request.Builder {
        return builder
    }

    /**
     * 构建新的请求
     *
     * @param builder 请求构建者
     * @return 新的请求
     */
    private fun createNewRequest(builder: Request.Builder): Request {
        return updateHeaders(builder).build()
    }

    companion object {
        private const val ACTION_GET = "GET"
        private const val ACTION_POST = "POST"
    }

}